package com.wwu.system.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wwu.common.enums.MenuBtnEnum;
import com.wwu.common.utils.AssertUtil;
import com.wwu.common.utils.PinyinUtils;
import com.wwu.common.web.ResultObj;
import com.wwu.common.web.ResultPage;
import com.wwu.system.common.utils.TreeUtils;
import com.wwu.system.common.security.SecurityUtil;
import com.wwu.system.entity.SysConfig;
import com.wwu.system.entity.SysMenu;
import com.wwu.system.mapper.SysMenuMapper;
import com.wwu.system.service.ISysConfigService;
import com.wwu.system.service.ISysMenuRoleService;
import com.wwu.system.service.ISysMenuService;
import com.wwu.system.vo.MenuVO;
import com.wwu.system.vo.XmSelectVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 *  系统菜单服务实现类
 * @author 一蓑烟雨
 * @date 2021-10-26
 * @version 1.0.0
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Resource
    private ISysConfigService sysConfigService;
    @Resource
    private ISysMenuRoleService sysMenuRoleService;

    /**
     * 根据菜单名、父菜单Id获取菜单
     * @param menuName   菜单名
     * @param menuName 菜单名
     * @return com.wwu.system.entity.SysMenu
     * @author 一蓑烟雨
     * @date 2023/6/12 11:20
     */
    @Override
    public SysMenu getMenuByMenuName(String menuName,String parentMenuId) {
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysMenu::getMenuName ,menuName)
                .eq(SysMenu::getMenuStatus,0);

        if(StringUtils.hasText(parentMenuId)){
            wrapper.eq(SysMenu::getParentMenuId,parentMenuId);
        }

        return this.getOne(wrapper);
    }

    /**
     * 根据菜单名获取菜单
     * @param menuAuth   菜单权限
     * @return com.wwu.system.entity.SysMenu
     * @author 一蓑烟雨
     * @date 2023/6/28 10:38
     */
    @Override
    public SysMenu getMenuByMenuAuth(String menuAuth) {
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysMenu::getMenuAuth ,menuAuth)
                .eq(SysMenu::getMenuStatus,0);

        return this.getOne(wrapper);
    }

    /**
     * 根据父菜单id查询子菜单id
     * @param parentMenuIds 父菜单Id集合
     * @return java.util.List<java.lang.String>
     * @author 一蓑烟雨
     * @date 2023/7/6 11:38
     */
    @Override
    public List<String> getChildMenuIdsByPid(List<String> parentMenuIds) {
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(SysMenu::getParentMenuId ,parentMenuIds);

        return this.list(wrapper).stream().map(SysMenu::getMenuId).collect(Collectors.toList());
    }

    /**
     * 分页询菜单信息
     * @param pageNum  页码
     * @param pageSize  每页数量
     * @param sysMenu 菜单实体
     * @return com.wwu.common.web.ResultObj
     * @author 一蓑烟雨
     * @date 2023/6/21 8:34
     */
    @Override
    public ResultPage<SysMenu> getMenuListPage(Integer pageNum, Integer pageSize, SysMenu sysMenu) {
        Page<SysMenu> page = new Page<>(pageNum,pageSize);
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        if(StringUtils.hasText(sysMenu.getMenuName())){
            wrapper.like(SysMenu::getMenuName,sysMenu.getMenuName());
        }
        if(null != sysMenu.getMenuStatus()){
            wrapper.eq(SysMenu::getMenuStatus,sysMenu.getMenuStatus());
        }
        wrapper.orderByAsc(SysMenu::getMenuType).orderByAsc(SysMenu::getMenuSort);

        IPage<SysMenu> sysMenuPage = this.page(page, wrapper);
        return new ResultPage<>(sysMenuPage);
    }

    /**
     * 根据菜单id查询菜单的父菜单树
     * @param menuId  菜单Id
     * @param menuType 菜单类型(0:模块;1:目录;2:菜单;3:按钮)
     * @return com.wwu.common.web.ResultObj
     * @author 一蓑烟雨
     * @date 2023/6/27 11:09
     */
    @Override
    public ResultObj parentMenuTree(String menuId, Integer menuType) {
        //查询所有菜单
        List<SysMenu> menuList = this.getMenuListPage(1,5000,new SysMenu()).getData();
        AssertUtil.isTrue(ObjectUtils.isEmpty(menuList),"菜单信息查询失败！");
        List<XmSelectVO> parentMenuList = new ArrayList<>();
        List<XmSelectVO> childMenuList = new ArrayList<>();

        //取出模块根节点
        for(SysMenu sysMenu: menuList){
            if(sysMenu.getMenuType().equals(0)){
                XmSelectVO xmSelect = new XmSelectVO();
                xmSelect.setValue(sysMenu.getMenuId());
                xmSelect.setName(sysMenu.getMenuName());
                xmSelect.setSort(sysMenu.getMenuSort());
                xmSelect.setDisabled(0 == sysMenu.getMenuStatus() ? false: true);
                xmSelect.setParentId(sysMenu.getParentMenuId());
                if(sysMenu.getMenuId().equals(menuId)){
                    xmSelect.setSelected(true);
                }
                parentMenuList.add(xmSelect);
            }
        }

        //遍历模块根节点的子节点
        if(!menuType.equals(0)){
            for(XmSelectVO xmSelect: parentMenuList){
                childMenuList = TreeUtils.getChildrenSelect(xmSelect.getValue(),menuId,menuType,menuList);
                xmSelect.setChildren(childMenuList);
            }
        }
        return ResultObj.success("当前菜单的父菜单查询成功",parentMenuList);
    }

    /**
     * 添加菜单信息
     * @param sysMenuJson 菜单相关信息
     * @author 一蓑烟雨
     * @date 2023/6/13 21:57
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void saveMenu(JSONObject sysMenuJson) {
        //获取菜单信息并添加菜单
        SysMenu sysMenu = JSONObject.parseObject(sysMenuJson.getString("menuData"), SysMenu.class);
        AssertUtil.isTrue(!StringUtils.hasText(sysMenu.getMenuName()),"菜单名不能为空");
        //模块不用判断权限
        AssertUtil.isTrue(!sysMenu.getMenuType().equals(0) && !StringUtils.hasText(sysMenu.getMenuAuth()),"菜单权限不能为空");
        AssertUtil.isTrue(!sysMenu.getMenuType().equals(0) && null != this.getMenuByMenuAuth(sysMenu.getMenuAuth()),"菜单权限已存在");
        //菜单判断URL
        AssertUtil.isTrue(sysMenu.getMenuType().equals(2) && !StringUtils.hasText(sysMenu.getMenuUrl()),"菜单URL不能为空");
        //若是按钮则需要判断当前父节点下是否菜单名重复
        AssertUtil.isTrue(sysMenu.getMenuType().equals(3) && null != this.getMenuByMenuName(sysMenu.getMenuName(),sysMenu.getParentMenuId()),"按钮已存在");

        sysMenu.setMenuTarget(sysMenu.getMenuType().equals(3) ? "_self" : sysMenu.getMenuTarget());
        sysMenu.setParentMenuId(sysMenu.getMenuType().equals(0) ? "-1" : sysMenu.getParentMenuId());
        sysMenu.setMenuStatus(0);
        sysMenu.setPy(PinyinUtils.getPinYin(sysMenu.getMenuName()));
        sysMenu.setWb(PinyinUtils.getWuBi(sysMenu.getMenuName()));
        AssertUtil.isTrue(!this.save(sysMenu),"菜单信息添加失败！");

        //获取菜单按钮信息并添加菜单按钮
        HashMap<String,String> menuBtnMap = JSONObject.parseObject(sysMenuJson.getString("menuBtn"), HashMap.class);
        if(!ObjectUtils.isEmpty(menuBtnMap)){
            List<SysMenu> sysMenuBtnList = new ArrayList<>();
            for(String key: menuBtnMap.keySet()){
                SysMenu sysMenuBtn =  new SysMenu();
                sysMenuBtn.setMenuType(3);
                sysMenuBtn.setMenuTarget("_self");
                sysMenuBtn.setMenuStatus(0);
                sysMenuBtn.setParentMenuId(sysMenu.getMenuId());
                if(MenuBtnEnum.SHOW.getCode().equals(key)){
                    sysMenuBtn.setMenuName(MenuBtnEnum.SHOW.getName());
                    sysMenuBtn.setMenuAuth(sysMenu.getMenuAuth().substring(0,sysMenu.getMenuAuth().lastIndexOf(":")+1)+ MenuBtnEnum.SHOW.getCode());
                    sysMenuBtn.setMenuSort(MenuBtnEnum.SHOW.getSort());
                    sysMenuBtn.setPy(PinyinUtils.getPinYin(MenuBtnEnum.SHOW.getName()));
                    sysMenuBtn.setWb(PinyinUtils.getWuBi(MenuBtnEnum.SHOW.getName()));

                    sysMenuBtnList.add(sysMenuBtn);
                }else if(MenuBtnEnum.ADD.getCode().equals(key)){
                    sysMenuBtn.setMenuName(MenuBtnEnum.ADD.getName());
                    sysMenuBtn.setMenuAuth(sysMenu.getMenuAuth().substring(0,sysMenu.getMenuAuth().lastIndexOf(":")+1)+ MenuBtnEnum.ADD.getCode());
                    sysMenuBtn.setMenuSort(MenuBtnEnum.ADD.getSort());
                    sysMenuBtn.setPy(PinyinUtils.getPinYin(MenuBtnEnum.ADD.getName()));
                    sysMenuBtn.setWb(PinyinUtils.getWuBi(MenuBtnEnum.ADD.getName()));

                    sysMenuBtnList.add(sysMenuBtn);
                }else if(MenuBtnEnum.EDIT.getCode().equals(key)){
                    sysMenuBtn.setMenuName(MenuBtnEnum.EDIT.getName());
                    sysMenuBtn.setMenuAuth(sysMenu.getMenuAuth().substring(0,sysMenu.getMenuAuth().lastIndexOf(":")+1)+ MenuBtnEnum.EDIT.getCode());
                    sysMenuBtn.setMenuSort(MenuBtnEnum.EDIT.getSort());
                    sysMenuBtn.setPy(PinyinUtils.getPinYin(MenuBtnEnum.EDIT.getName()));
                    sysMenuBtn.setWb(PinyinUtils.getWuBi(MenuBtnEnum.EDIT.getName()));

                    sysMenuBtnList.add(sysMenuBtn);
                }else if(MenuBtnEnum.DEL.getCode().equals(key)){
                    sysMenuBtn.setMenuName(MenuBtnEnum.DEL.getName());
                    sysMenuBtn.setMenuAuth(sysMenu.getMenuAuth().substring(0,sysMenu.getMenuAuth().lastIndexOf(":")+1)+ MenuBtnEnum.DEL.getCode());
                    sysMenuBtn.setMenuSort(MenuBtnEnum.DEL.getSort());
                    sysMenuBtn.setPy(PinyinUtils.getPinYin(MenuBtnEnum.DEL.getName()));
                    sysMenuBtn.setWb(PinyinUtils.getWuBi(MenuBtnEnum.DEL.getName()));

                    sysMenuBtnList.add(sysMenuBtn);
                }else if(MenuBtnEnum.STATUS.getCode().equals(key)){
                    sysMenuBtn.setMenuName(MenuBtnEnum.STATUS.getName());
                    sysMenuBtn.setMenuAuth(sysMenu.getMenuAuth().substring(0,sysMenu.getMenuAuth().lastIndexOf(":")+1)+ MenuBtnEnum.STATUS.getCode());
                    sysMenuBtn.setMenuSort(MenuBtnEnum.STATUS.getSort());
                    sysMenuBtn.setPy(PinyinUtils.getPinYin(MenuBtnEnum.STATUS.getName()));
                    sysMenuBtn.setWb(PinyinUtils.getWuBi(MenuBtnEnum.STATUS.getName()));

                    sysMenuBtnList.add(sysMenuBtn);
                }else if(MenuBtnEnum.EXPORT.getCode().equals(key)){
                    sysMenuBtn.setMenuName(MenuBtnEnum.EXPORT.getName());
                    sysMenuBtn.setMenuAuth(sysMenu.getMenuAuth().substring(0,sysMenu.getMenuAuth().lastIndexOf(":")+1)+ MenuBtnEnum.EXPORT.getCode());
                    sysMenuBtn.setMenuSort(MenuBtnEnum.EXPORT.getSort());
                    sysMenuBtn.setPy(PinyinUtils.getPinYin(MenuBtnEnum.EXPORT.getName()));
                    sysMenuBtn.setWb(PinyinUtils.getWuBi(MenuBtnEnum.EXPORT.getName()));

                    sysMenuBtnList.add(sysMenuBtn);
                }
            }

            if(!ObjectUtils.isEmpty(sysMenuBtnList)){
                AssertUtil.isTrue(!this.saveBatch(sysMenuBtnList),"菜单对应的按钮添加失败");
            }
        }
    }

    /**
     * 更新菜单信息
     * @param sysMenu 菜单信息
     * @author 一蓑烟雨
     * @date 2023/6/2 17:20
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void updateMenu(SysMenu sysMenu) {
        AssertUtil.isTrue(!StringUtils.hasText(sysMenu.getMenuName()),"菜单名不能为空");
        //模块不用判断权限
        if(!sysMenu.getMenuType().equals(0)){
            AssertUtil.isTrue(!StringUtils.hasText(sysMenu.getMenuAuth()),"菜单权限不能为空");
            SysMenu sysMenuByAuth = this.getMenuByMenuAuth(sysMenu.getMenuAuth());
            AssertUtil.isTrue(null != sysMenuByAuth && !sysMenu.getMenuId().equals(sysMenuByAuth.getMenuId()),"菜单权限已存在");
        }
        //菜单判断URL
        AssertUtil.isTrue(sysMenu.getMenuType().equals(2) && !StringUtils.hasText(sysMenu.getMenuUrl()),"菜单URL不能为空");
        //若是按钮则需要判断当前父节点下是否菜单名重复
        AssertUtil.isTrue(sysMenu.getMenuType().equals(3) && null != this.getMenuByMenuName(sysMenu.getMenuName(),sysMenu.getParentMenuId()),"按钮已存在");

        sysMenu.setPy(PinyinUtils.getPinYin(sysMenu.getMenuName()));
        sysMenu.setWb(PinyinUtils.getWuBi(sysMenu.getMenuName()));
        AssertUtil.isTrue(!this.updateById(sysMenu),"菜单信息更新失败");
    }

    /**
     * 更新菜单及子菜单状态
     * @param menuId  菜单编码
     * @param menuStatus 菜菜单状态(0:启用;1:作废)
     * @author 一蓑烟雨
     * @date 2023/6/28 20:54
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void updateMenuStatus(String menuId, Integer menuStatus) {
        AssertUtil.isTrue(!StringUtils.hasText(menuId),"菜单编码不能为空");
        AssertUtil.isTrue(ObjectUtils.isEmpty(menuStatus),"菜单状态不能为空");
        AssertUtil.isTrue(null == this.getById(menuId),"当前菜单不存在");

        List<String> menuIds = new ArrayList<>();
        List<String> childMenuIds = new ArrayList<>();
        menuIds.add(menuId);
        childMenuIds.add(menuId);
        boolean childMenuFlag = true;
        while (childMenuFlag) {
            childMenuIds = this.getChildMenuIdsByPid(childMenuIds);
            if(!ObjectUtils.isEmpty(childMenuIds)){
                menuIds.addAll(childMenuIds);
            }else {
                childMenuFlag = false;
            }
        }
        LambdaUpdateWrapper<SysMenu> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(SysMenu::getMenuStatus,menuStatus)
                .in(SysMenu::getParentMenuId,menuIds)
                .or()
                .eq(SysMenu:: getMenuId,menuId);
        AssertUtil.isTrue(!this.update(wrapper),"菜单状态更新失败！");
    }

    /**
     *  删除菜单及子菜单信息(物理删除)
     * @param menuIds
     * @author 一蓑烟雨
     * @date 2023/6/12 21:44
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void deleteMenu(List<String> menuIds) {
        AssertUtil.isTrue(ObjectUtils.isEmpty(menuIds),"请选择删除的记录！");

        List<String> delMenuIds = new ArrayList<>();
        List<String> childMenuIds = new ArrayList<>();
        delMenuIds.addAll(menuIds);
        childMenuIds.addAll(menuIds);
        boolean childMenuFlag = true;
        while (childMenuFlag) {
            childMenuIds = this.getChildMenuIdsByPid(childMenuIds);
            if(!ObjectUtils.isEmpty(childMenuIds)){
                delMenuIds.addAll(childMenuIds);
            }else {
                childMenuFlag = false;
            }
        }
        //删除菜单角色信息
        AssertUtil.isTrue(!sysMenuRoleService.deleteMenuRoleByMenuIds(delMenuIds),"菜单角色信息删除失败");

        //删除菜单信息
        AssertUtil.isTrue(!this.removeByIds(delMenuIds),"菜单及子菜单信息删除失败");
    }

    /**
     * 动态获取菜单
     * @return java.lang.String
     * @author 一蓑烟雨
     * @date 2023/6/30 15:13
     */
    @Override
    public String initMenu() {
        AssertUtil.isTrue(ObjectUtils.isEmpty(SecurityUtil.getCurrUser()),"当前用户不能为空");
        HashMap<Object, Object> menuMap = new LinkedHashMap<>();
        LinkedHashMap<Object, Object> homeMap = new LinkedHashMap<>();
        LinkedHashMap<Object, Object> logoMap = new LinkedHashMap<>();

        //查询配置信息
        SysConfig sysConfig = sysConfigService.getSysConfig();
        //根据用户Id查询菜单
        List<MenuVO> menuList = sysMenuMapper.getMenuByUserId(SecurityUtil.getCurrUserId());

        homeMap.put("title","首页");
        homeMap.put("href","welcome");
        menuMap.put("homeInfo",homeMap);

        logoMap.put("title",sysConfig.getSystemName());
        logoMap.put("image",sysConfig.getSystemLogo());
        logoMap.put("href","");
        menuMap.put("logoInfo",logoMap);

        menuMap.put("menuInfo",TreeUtils.getChildMenu("-1",menuList));
        return JSONObject.toJSONString(menuMap);
    }
}
